package com.sisgh.jefdv1.core.exec.impl;

import com.sisgh.jefdv1.core.entity.Entities;
import com.sisgh.jefdv1.core.exec.Executor;
import com.sisgh.jefdv1.core.entity.Entity;
import com.sisgh.jefdv1.core.java.format.JFPBuilder;
import com.sisgh.jefdv1.core.java.format.JavaFormat;
import com.sisgh.jefdv1.core.java.format.JavaFormatProcessor;
import com.sisgh.jefdv1.core.java.keyword.imports.impl.Annotation;
import com.sisgh.jefdv1.core.java.keyword.imports.impl.Extend;
import com.sisgh.jefdv1.core.java.keyword.pack.Package;
import com.sisgh.jefdv1.core.jdbc.connection.factory.ConnectionFactory;
import com.sisgh.jefdv1.core.jdbc.connection.factory.impl.MySQLConnectionFactory;
import com.sisgh.jefdv1.core.jdbc.connection.properties.ConnectionProperties;
import com.sisgh.jefdv1.core.jdbc.table.Table;
import com.sisgh.jefdv1.core.jdbc.table.Tables;
import com.sisgh.jefdv1.exception.common.ClosedException;
import com.sisgh.jefdv1.exception.exec.InvalidDBException;
import com.sisgh.jefdv1.exception.file.JavaFileException;
import com.sisgh.jefdv1.file.JavaFileWriter;
import com.sisgh.jefdv1.util.CloseUtils;
import com.sisgh.jefdv1.util.StringUtils;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class MySQLExecutor implements Executor, AutoCloseable {

    private static MySQLExecutor executor;

    private final String tag;
    private final Connection conn;

    private boolean isSame(ConnectionProperties cp) {
        String shortestUrl = cp.getShortestUrl();
        return shortestUrl.equals(tag);
    }

    public static MySQLExecutor getInstance() {
        return executor;
    }

    public static MySQLExecutor newInstance(ConnectionFactory cf,
                                            ConnectionProperties cp)
            throws InvalidDBException, SQLException, ClosedException {

        if (!(cf instanceof MySQLConnectionFactory)) {
            String msg = StringUtils.format("非法的数据库类型：{}", cf.getDBType());
            throw new InvalidDBException(msg);
        }

        if (executor != null) {
            if (executor.isSame(cp)) {
                return executor;
            }
            executor.close();
        }

        Connection connection = cf.getConnection(cp);

        synchronized (MySQLExecutor.class) {
            if (executor == null) {
                executor = new MySQLExecutor(connection, cp);
            }
        }

        return executor;
    }

    private MySQLExecutor(Connection connection,
                          ConnectionProperties cp) {
        this.conn = connection;
        this.tag = cp.getShortestUrl();
    }

    @Override
    public void close() throws ClosedException {
        CloseUtils.close(conn);
    }

    @Override
    public List<String> showDBs() throws SQLException {
        DatabaseMetaData metaData = conn.getMetaData();

        ResultSet catalogs = metaData.getCatalogs();

        List<String> tables = new ArrayList<>();
        while (catalogs.next()) {
            String table = catalogs.getString("TABLE_CAT");
            tables.add(table);
        }
        return tables;
    }

    @Override
    public List<Table> getTables() throws SQLException {
        if (StringUtils.isNullOrEmpty(conn.getCatalog())) {
            throw new SQLException("请选择数据库后再进行相关操作");
        }

        return Tables.fetchTables(conn);
    }

    @Override
    public List<Entity> toEntities(List<Table> tables) throws SQLException {
        return Entities.convertToEntities(tables);
    }

    @Override
    public void writeFiles(List<Entity> entities,
                           Package pack,
                           List<Annotation> annotations,
                           Extend extend,
                           boolean foldersByPackage
                           ) throws JavaFileException {
        JavaFormatProcessor jfp = JFPBuilder.builder()
                .packagePath(pack)
                .annotations(annotations.toArray(new Annotation[0]))
                .extend(extend)
                .build();

        List<JavaFormat> formats = jfp.formats(entities);

        JavaFileWriter javaFileWriter = JavaFileWriter.defaultWriter(formats).foldersByPackage(foldersByPackage);
        javaFileWriter.convertJavaFile();
    }
}
